package com.qf.myrbac.sys.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.qf.myrbac.sys.entity.SysMenu;
import com.qf.myrbac.sys.dao.SysMenuDao;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qf.myrbac.sys.entity.SysRoleMenu;
import com.qf.myrbac.sys.entity.SysUserRole;
import com.qf.myrbac.sys.service.SysMenuService;
import com.qf.myrbac.sys.service.SysRoleMenuService;
import com.qf.myrbac.sys.service.SysUserRoleService;
import com.qf.myrbac.utils.Constants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 菜单管理 服务实现类
 * </p>
 *
 * @author 徐英海
 * @since 2022-10-27
 */
@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuDao, SysMenu> implements SysMenuService {
    @Autowired
    private SysUserRoleService sysUserRoleService;
    @Autowired
    private SysRoleMenuService sysRoleMenuService;

    @Transactional(propagation = Propagation.SUPPORTS)
    @Override
    public Set<String> queryAdminPermSet() {
        QueryWrapper<SysMenu> menuQueryWrapper = new QueryWrapper<>();
        menuQueryWrapper.isNotNull("perms");
        List<SysMenu> menuList = this.list(menuQueryWrapper);
        List<String> permList = menuList.stream()
                .map(SysMenu::getPerms)
                .collect(Collectors.toList());
        //处理数据库字段中的权限字符串之间的逗号
        Set<String> permSet = new HashSet<>();
        for (String perm : permList) {
            if (perm.contains(",")) {
                permSet.addAll(Arrays.asList(perm.split(",")));
            } else {
                permSet.add(perm);
            }
        }
        return permSet;
    }

    @Transactional(propagation = Propagation.SUPPORTS)
    @Override
    public List<String> queryPermList(Long userId) {
        //根据用户id查询角色id
        QueryWrapper<SysUserRole> userRoleQueryWrapper = new QueryWrapper<>();
        userRoleQueryWrapper.select("role_id");
        userRoleQueryWrapper.eq("user_id", userId);
        List<Object> roleIdList = sysUserRoleService.listObjs(userRoleQueryWrapper);
        //查询roleIdList对应的menuIdList
        QueryWrapper<SysRoleMenu> roleMenuQueryWrapper = new QueryWrapper<>();
        roleMenuQueryWrapper.select("menu_id");
        roleMenuQueryWrapper.in("role_id", roleIdList);
        List<Object> menuIdList = sysRoleMenuService.listObjs(roleMenuQueryWrapper);
        //查询menuIdList对应的所有菜单信息List<权限字符串>
        QueryWrapper<SysMenu> menuQueryWrapper = new QueryWrapper<>();
        menuQueryWrapper.select("perms");
        menuQueryWrapper.in("menu_id", menuIdList);
        List<Object> permsList = this.listObjs(menuQueryWrapper);
        List<String> permsListNoRepeat = permsList.stream()
                .map(Object::toString)
                .flatMap(
                        perms -> Arrays.stream(perms.split(","))
                )
                .distinct()
                .collect(Collectors.toList());
        return permsListNoRepeat;
    }

    @Transactional(propagation = Propagation.SUPPORTS)
    @Override
    public List<SysMenu> getUserMenuList(Long userId) {
        List<SysMenu> allMenuList = null;
        List<SysMenu> menuList = null;
        if (userId.equals(Constants.SUPER_ADMIN_ID)) {
            allMenuList = this.list();
            menuList = organizeMenu(Constants.TOP_MENU_PARENT_ID, allMenuList);
        } else {
            allMenuList = queryAllMenyByUserId(userId);
            menuList = organizeMenu(Constants.TOP_MENU_PARENT_ID, allMenuList);
        }
        return menuList;
    }

    @Transactional(propagation = Propagation.SUPPORTS)
    @Override
    public List<SysMenu> queryAllMenyByUserId(Long userId) {
        //查询当前用户对应的所有角色
        QueryWrapper<SysUserRole> sysUserRoleQueryWrapper = new QueryWrapper<>();
        sysUserRoleQueryWrapper.select("role_id");
        sysUserRoleQueryWrapper.eq("user_id", userId);
        List<Object> roleIdList = sysUserRoleService.listObjs(sysUserRoleQueryWrapper);
        //查询 sys_role_menu 表，把 roleIdList 对于的所有 menu_id 查出来
        final QueryWrapper<SysRoleMenu> sysRoleMenuQueryWrapper = new QueryWrapper<>();
        sysRoleMenuQueryWrapper.select("menu_id");
        if (roleIdList != null && roleIdList.size() > 0) {
            sysRoleMenuQueryWrapper.in("role_id", roleIdList);
        }
        final List<Object> menuIdList = sysRoleMenuService.listObjs(sysRoleMenuQueryWrapper);
        //查询 menuIdList 对于的 所有 菜单
        final QueryWrapper<SysMenu> sysMenuQueryWrapper = new QueryWrapper<>();
        sysMenuQueryWrapper.in("menu_id", menuIdList);
        return this.list(sysMenuQueryWrapper);
    }

    /**
     * 当前用户的所有菜单，组织成层级结构
     *
     * @param topMenuParentId
     * @param allMenuList
     * @return
     */
    private List<SysMenu> organizeMenu(Long topMenuParentId, List<SysMenu> allMenuList) {
        List<SysMenu> sysList = allMenuList.stream()
                .filter(
                        menu -> menu.getParentId().equals(topMenuParentId)
                ).collect(Collectors.toList());
        populateChildMenu(sysList, allMenuList);
        return sysList;
    }

    private void populateChildMenu(List<SysMenu> sysList, List<SysMenu> allMenuList) {
        //遍历filterList，对于每一个菜单，查找该菜单是否有子菜单
        //如果有，继续递归该方法，为所有子菜单继续填充下级菜单
        for (SysMenu sysMenu : sysList) {
            List<SysMenu> childList = findChildMenu(sysMenu, allMenuList);
            if (childList != null && childList.size() > 0) {
                //为所有子菜单填充下级菜单
                populateChildMenu(childList, allMenuList);
                sysMenu.setList(childList);
            }
        }
    }

    private List<SysMenu> findChildMenu(SysMenu sysMenu, List<SysMenu> allMenuList) {
        return allMenuList.stream()
                .filter(
                        menu -> menu.getParentId().equals(sysMenu.getMenuId())
                ).collect(Collectors.toList());
    }
}
